﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;

namespace Lyon.MemoryShares
{
    /// <summary>
    /// 内存共享
    /// </summary>
    public class MemorySharing : IDisposable
    {
        string _Name;
        int _AllCapacity;
        int _MaxCapacity;
        int _HeaderSize;

        IntPtr _SharedMemoryFileHandle = IntPtr.Zero;
        IntPtr _MemoryHandle = IntPtr.Zero;
        Semaphore _WriteSemaphore;

        byte[] _CountBuffer;
        byte[] _AllDataBuffer;
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="name"></param>
        /// <param name="capacity"></param>
        public MemorySharing(string name, int capacity)
        {
            this._Name = name;
            this._MaxCapacity = capacity;
            this._HeaderSize = sizeof(int);
            this._AllCapacity = _HeaderSize + _MaxCapacity;

            InitWin32();
            InitLock();
            InitCatche();
            Clear();
        }
        private void InitWin32()
        {
            _SharedMemoryFileHandle = MemorySharingTool.CreateFileMapping(MemorySharingTool.INVALID_HANDLE_VALUE, IntPtr.Zero
                , (uint)MemorySharingTool.PAGE_READWRITE, 0, (uint)_AllCapacity, _Name);

            if (MemorySharingTool.GetLastError() == MemorySharingTool.ERROR_ALREADY_EXISTS) //已经创建
                _SharedMemoryFileHandle = MemorySharingTool.OpenFileMapping(MemorySharingTool.FILE_MAP_ALL_ACCESS, false, _Name);
            if (_SharedMemoryFileHandle == IntPtr.Zero)
                throw new Exception();

            //创建内存映射
            _MemoryHandle = MemorySharingTool.MapViewOfFile(_SharedMemoryFileHandle, MemorySharingTool.FILE_MAP_ALL_ACCESS, 0, 0, (uint)_AllCapacity);
            if (_MemoryHandle == IntPtr.Zero)
            {
                MemorySharingTool.CloseHandle(_SharedMemoryFileHandle);
                throw new Exception();
            }
            
        }
        private void InitLock()
        {
            //创建写入锁
            try
            {
                _WriteSemaphore = Semaphore.OpenExisting(_Name + ".WriteShareMemory");
            }
            catch
            {
                _WriteSemaphore = new Semaphore(1, 1, _Name + ".WriteShareMemory");
            }
        }
        private void InitCatche()
        {
            _CountBuffer = new byte[_HeaderSize];
            _AllDataBuffer = new byte[_AllCapacity];
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public int Read(byte[] buffer, int offset, int count)
        {
            int realCount = Count;
            if (count < realCount)
                realCount = count;
            if (realCount > 0)
            {
                _WriteSemaphore.WaitOne(1000);
                Marshal.Copy(_MemoryHandle, _AllDataBuffer, 0, _HeaderSize + realCount);
                _WriteSemaphore.Release();

                Array.Copy(_AllDataBuffer, _HeaderSize, buffer, offset, realCount);
            }
            return realCount;
        }
        /// <summary>
        /// 写入数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        public void Write(byte[] buffer, int offset, int count)
        {
            int realCount = count < _MaxCapacity ? count : _MaxCapacity;

            Byte[] countData = BitConverter.GetBytes(realCount);
            countData.CopyTo(_AllDataBuffer, 0);

            Array.Copy(buffer, offset, _AllDataBuffer, _HeaderSize, realCount);

            _WriteSemaphore.WaitOne(1000);
            Marshal.Copy(_AllDataBuffer, 0, _MemoryHandle, _HeaderSize + realCount);
            _WriteSemaphore.Release();
        }
        /// <summary>
        /// 释放非托管资源
        /// </summary>
        public void Dispose()
        {
            MemorySharingTool.UnmapViewOfFile(_MemoryHandle);
            try
            {
                MemorySharingTool.CloseHandle(_SharedMemoryFileHandle);
            }
            finally { }
        }

        /// <summary>
        /// 数据大小(如果为-1，则表示无数据)
        /// </summary>
        public int Count
        {
            get
            {
                _WriteSemaphore.WaitOne(1000);
                Marshal.Copy(_MemoryHandle, _CountBuffer, 0, _CountBuffer.Length);
                _WriteSemaphore.Release();
                return BitConverter.ToInt32(_CountBuffer, 0);
            }
        }

        /// <summary>
        /// 内存大小
        /// </summary>
        public int Capacity
        {
            get
            {
                return _MaxCapacity;
            }
        }
        /// <summary>
        /// 清除数据
        /// </summary>
        public void Clear()
        {
            Byte[] countData = BitConverter.GetBytes(-1);
            countData.CopyTo(_AllDataBuffer, 0);

            _WriteSemaphore.WaitOne(1000);
            Marshal.Copy(_AllDataBuffer, 0, _MemoryHandle, _HeaderSize);
            _WriteSemaphore.Release();
        }

        static class MemorySharingTool
        {
            [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName);

            [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName);

            [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);

            [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
            public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress);

            [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
            public static extern bool CloseHandle(IntPtr handle);

            [DllImport("kernel32", EntryPoint = "GetLastError")]
            public static extern int GetLastError();

            public const int ERROR_ALREADY_EXISTS = 183;
            public const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;
            public const int PAGE_READWRITE = 0x04;
            public const int INVALID_HANDLE_VALUE = -1;
        }
    }
}